home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 11 - 1995 / 11.04 Apr 95 / TreeAppƒ / Eric's C++ Libraries / Interface Classes / CPPPackedTreeNode.cp < prev    next >
Encoding:
Text File  |  1996-04-04  |  29.0 KB  |  903 lines  |  [TEXT/KAHL]

  1. /***************************************************** IMPLEMENTATION
  2.     DATE:    10/28/93
  3.     AUTHOR: Eric R. Rosé
  4.  
  5.     CLASS:  CPPPackedTreeNode
  6.     
  7.     SUPERCLASS: CPPTreeNode
  8.     
  9.         This C++ class manages the visual representation of a node
  10.         in a hierarchical tree
  11.     
  12. ********************************************************************/
  13.  
  14. #include <CPPPackedTreeNode.h>
  15. #include <CPPVisualTree.h>
  16. #include "CPPString.h"
  17. #include <MathTools.h>
  18. #include <ToolboxTools.h>
  19. #include <math.h>
  20.  
  21.  
  22. #define    hMargin    2
  23. #define    vMargin    2
  24.  
  25. short *Contour = NULL;
  26.  
  27. extern     void    SetNotYetPlaced (CPPTreeNode *theNode, long param);
  28.  
  29. /*-----------------------------------------------------------------*/
  30. /*------------------------ PUBLIC METHODS -------------------------*/
  31. /*-----------------------------------------------------------------*/
  32.  
  33.     CPPPackedTreeNode::CPPPackedTreeNode (CPPObject *NodeData, 
  34.                                           CPPTree *BelongsTo, 
  35.                                           Boolean becomeOwner,
  36.                                           Boolean selected) :
  37.                        CPPVisualTreeNode (NodeData, BelongsTo, becomeOwner, selected)
  38.     {
  39.         SetPt (&this->nodeSize, 0, 0);
  40.         this->familySize = this->childSize = this->nodeSize;
  41.         
  42.         SetRect (&this->nodeRect, 0, 0, 0, 0);
  43.         this->familyRect = this->childRect = this->nodeRect;
  44.         
  45.         SetPt (&this->estTopLeft, 0, 0);
  46.         
  47.         this->needsDraw = TRUE;
  48.         this->needsResize = TRUE;
  49.         this->isSelected = selected;
  50.     }
  51.  
  52. /*-----------------------------------------------------------------*/
  53.  
  54.     CPPPackedTreeNode::~CPPPackedTreeNode (void)
  55.     {
  56.  
  57.     }
  58.             
  59. /*-----------------------------------------------------------------*/
  60.  
  61.     Boolean    CPPPackedTreeNode::Member (char *className)
  62.     {
  63.         if (strcmp(className, CPPPackedTreeNode::ClassName()) == 0)
  64.           return TRUE;
  65.         else
  66.           return CPPVisualTreeNode::Member(className);
  67.     }
  68.  
  69. /*-----------------------------------------------------------------*/
  70.  
  71.     char     *CPPPackedTreeNode::ClassName (void)
  72.     {
  73.         return "CPPPackedTreeNode";
  74.     }
  75.  
  76. /*-----------------------------------------------------------------*/
  77.  
  78.     CPPObject *CPPPackedTreeNode::Clone(void)
  79.     {
  80.         return NULL;
  81.     }
  82.     
  83. /*-----------------------------------------------------------------*/
  84.  
  85.     void    CPPPackedTreeNode::EraseNode (Boolean wholeFamily)
  86.     {
  87.         PenState    OldState;
  88.         
  89.         // prepare the grafport to draw tree nodes
  90.         if (this->Root)
  91.           ((CPPVisualTree *)this->Root)->Prepare(this);
  92.         
  93.         EraseRect (&this->nodeRect);
  94.         EraseJoin((CPPVisualTreeNode *)this->Parent, this);
  95.  
  96.         if (wholeFamily)
  97.           {
  98.               for (long i = 1; i <= numItems; i++)
  99.                 ((CPPPackedTreeNode *)NthChild (i))->EraseNode(wholeFamily);
  100.           }
  101.  
  102.         // restore the original grafport state
  103.         ((CPPVisualTree *)this->Root)->Restore(this);
  104.     }
  105.     
  106. /*-----------------------------------------------------------------*/
  107.  
  108.     void     CPPPackedTreeNode::DoCalcFamilySize(void)
  109.     /* Calculate how large the node's children, and the entire node */
  110.     /* are, storing the values in childSize and familySize */
  111.     {
  112.         CPPPackedTreeNode *theChild;
  113.         orientStyle    Orient = GetOrientation();
  114.         short        betweenNodeMargin = GetNodeMargin();
  115.         
  116.         if (!this->needsResize) return;
  117.         this->needsResize = FALSE;
  118.         this->CalcNodeSize();            // figure out node's size first
  119.         
  120.         
  121.         SetPt (&this->childSize, 0, 0);    // assume we have no children
  122.         
  123.         // iterate over all the children of this node, accumulating
  124.         // their height and width
  125.         for (long i = 1; i<= this->numItems; i++)
  126.           {
  127.               theChild = (CPPPackedTreeNode *)NthChild(i);
  128.               theChild->DoCalcFamilySize();
  129.               if ((Orient == kTopDown) || (Orient == kBottomUp))
  130.                 {
  131.                     childSize.h += theChild->nodeSize.h + ((i != this->numItems) ? betweenNodeMargin : 0);
  132.                     childSize.v = Max (theChild->nodeSize.v, childSize.v);
  133.                 }
  134.               else
  135.                 {
  136.                     childSize.v += theChild->nodeSize.v + ((i != this->numItems) ? betweenNodeMargin : 0);
  137.                     childSize.h = Max (theChild->nodeSize.h, childSize.h);
  138.                 }
  139.           }
  140.         
  141.         // Set the size of the family - a combination of the node size,
  142.         // child size, and branch length;
  143.         if ((Orient == kTopDown) || (Orient == kBottomUp))
  144.           {
  145.               familySize.h = Max(childSize.h, nodeSize.h);
  146.               familySize.v = nodeSize.v;
  147.               if (childSize.v > 0)
  148.                 familySize.v += childSize.v + GetBranchLength();
  149.           }
  150.         else
  151.           {
  152.               familySize.v = Max(childSize.v, nodeSize.v);
  153.               familySize.h = nodeSize.h;
  154.               if (childSize.h > 0)
  155.                 familySize.h += childSize.h + GetBranchLength();
  156.           }
  157.           
  158.     }
  159.  
  160. /*-----------------------------------------------------------------*/
  161.  
  162.     void    CPPPackedTreeNode::CPBTopTraverse (short level, short levelOffset)
  163.     /* the recursive traversal part of the CalcFamilyBounds for */
  164.     /* vertically oriented trees */
  165.     {
  166.         CPPPackedTreeNode    *theChild;
  167.         short    childWidth = this->childSize.h;
  168.         long    i;
  169.         justStyle    Just    =    GetJustification();
  170.         orientStyle    Orient    =    GetOrientation();
  171.         Rect        emptyRect = {0,0,0,0};
  172.         Boolean    eraseKidJoins = FALSE;
  173.         Rect    OldNodeRect = this->nodeRect,
  174.                 OldGChildRect = this->gChildRect;
  175.         Rect    newNodeRect = {0,0,0,0},
  176.                 newFamilyRect = {0,0,0,0},
  177.                 newChildRect = {0,0,0,0},
  178.                 newGChildRect = {0,0,0,0};
  179.  
  180.         // make sure the node's estimate is below the lowest node
  181.         // on this level
  182.         this->estTopLeft.h = Max (this->estTopLeft.h, Contour[level]);
  183.         
  184.         // if the node has children, iterate over them, giving each an estimated
  185.         // position and then calling this routine recursively
  186.         if (this->numItems)
  187.           {
  188.             for (i = 1; i <= this->numItems; i++)
  189.               {
  190.                   theChild = (CPPPackedTreeNode *)NthChild(i);
  191.                   if (theChild)
  192.                     {
  193.                         if (i == 1)
  194.                           {
  195.                               // estimate the top left corner of the first child node
  196.                             switch (Just) {
  197.                                 case kJustCenter:
  198.                                     SetPt (&theChild->estTopLeft, 
  199.                                             this->estTopLeft.h + (this->nodeSize.h / 2) - (childWidth / 2),
  200.                                               this->estTopLeft.v + levelOffset);
  201.                                     break;
  202.                                     
  203.                                 case kJustLeft:
  204.                                     SetPt (&theChild->estTopLeft,
  205.                                             this->estTopLeft.h, this->estTopLeft.v + levelOffset);
  206.                                     break;
  207.                                     
  208.                                 case kJustRight:
  209.                                     SetPt (&theChild->estTopLeft,
  210.                                             this->estTopLeft.h + this->nodeSize.h - childWidth, 
  211.                                             this->estTopLeft.v + levelOffset);
  212.                                     break;
  213.                             }
  214.                             
  215.                             // place its children
  216.                             theChild->CPBTopTraverse(level+1, Max (this->childSize.v+5, GetBranchLength()));        
  217.                             // accumulate it into the rect we will use to center ourselves
  218.                             newGChildRect = theChild->nodeRect;
  219.                               // accumulate the child into the rect enclosing all children
  220.                             newChildRect = theChild->familyRect;
  221.                           }
  222.                         else
  223.                           {
  224.                               SetPt(&theChild->estTopLeft, 
  225.                                    Contour[level+1],
  226.                                    this->estTopLeft.v + levelOffset);
  227.                             
  228.                             // place its children
  229.                             theChild->CPBTopTraverse(level+1, Max (this->childSize.v+5, GetBranchLength()));
  230.                             // accumulate it into the rect we will use to center ourselves
  231.                               UnionRect (&newGChildRect, &theChild->nodeRect, &newGChildRect);
  232.                               // accumulate the child into the rect enclosing all children
  233.                               UnionRect (&newChildRect, &theChild->familyRect, &newChildRect);
  234.                           }
  235.                     }
  236.               }
  237.           }
  238.  
  239.         // set the node's rectangle
  240.         SetRect (&newNodeRect, this->estTopLeft.h, this->estTopLeft.v,
  241.                    this->estTopLeft.h + this->nodeSize.h,
  242.                    this->estTopLeft.v + this->nodeSize.v);
  243.         
  244.         // justify the node with respect to its children
  245.         if (this->numItems)
  246.           {
  247.                 switch (Just) {
  248.                   case kJustCenter:
  249.                       CenterRect (&newNodeRect, &newGChildRect, TRUE, FALSE);
  250.                       break;
  251.                   case kJustRight:
  252.                       AlignRect (&newNodeRect, &newGChildRect, kRight, kNone);
  253.                       break;
  254.                   case kJustLeft:
  255.                       AlignRect (&newNodeRect, &newGChildRect, kLeft, kNone);
  256.                       break;
  257.                 }
  258.                 
  259.             // build the rectangle which encloses the node and its children
  260.             UnionRect (&newNodeRect, &newChildRect, &newFamilyRect);
  261.           }
  262.         else
  263.           newFamilyRect = newNodeRect;
  264.           
  265. // this routine is the same for both the packed and visual tree nodes
  266.  
  267.         // if the node has changed position or size, erase it and
  268.         // note that it must be redrawn
  269.             if (!EqualRect (&OldNodeRect, &newNodeRect))
  270.               {
  271.                   if (!EqualRect (&emptyRect, &OldNodeRect))
  272.                     EraseNode (FALSE);
  273.                 this->needsDraw = eraseKidJoins = TRUE;
  274.               }
  275.             else    // if the node's children have moved, redraw it too
  276.             if (Just != kJustCenter)
  277.               {
  278.                   if ((Orient == kBottomUp) || (Orient == kTopDown))
  279.                     {
  280.                         if (OldGChildRect.right - OldGChildRect.left != 
  281.                             newGChildRect.right - newGChildRect.left)
  282.                           this->needsDraw = eraseKidJoins = TRUE;
  283.                     }
  284.                   else
  285.                     {
  286.                         if (OldGChildRect.bottom - OldGChildRect.top != 
  287.                             newGChildRect.bottom - newGChildRect.top)
  288.                           this->needsDraw =  eraseKidJoins = TRUE;
  289.                     }
  290.               }
  291.               
  292.             if (eraseKidJoins)
  293.               for (i = 1; i <= this->numItems; i++)
  294.                 EraseJoin (this, (CPPVisualTreeNode *)NthChild(i));
  295.  
  296.          this->nodeRect = newNodeRect;
  297.          this->childRect = newChildRect;
  298.          this->familyRect = newFamilyRect;
  299.          this->gChildRect = newGChildRect;
  300.  
  301.         this->notYetPlaced = FALSE;
  302.  
  303.         // update the value of the lowest point in the current level
  304.         Contour[level] = newNodeRect.right + GetNodeMargin();
  305.     }
  306.  
  307. /*-----------------------------------------------------------------*/
  308.  
  309.     void    CPPPackedTreeNode::CPBBottomTraverse (short level, short levelOffset)
  310.     /* the recursive traversal part of the CalcFamilyBounds for */
  311.     /* Bottom-Up oriented trees */
  312.     {
  313.         CPPPackedTreeNode    *theChild;
  314.         short    childWidth = this->childSize.h;
  315.         long    i;
  316.         justStyle    Just    =    GetJustification();
  317.         
  318.         orientStyle    Orient    =    GetOrientation();
  319.         Rect        emptyRect = {0,0,0,0};
  320.         Boolean    eraseKidJoins = FALSE;
  321.         Rect    OldNodeRect = this->nodeRect,
  322.                 OldGChildRect = this->gChildRect;
  323.         Rect    newNodeRect = {0,0,0,0},
  324.                 newFamilyRect = {0,0,0,0},
  325.                 newChildRect = {0,0,0,0},
  326.                 newGChildRect = {0,0,0,0};
  327.  
  328.         // make sure the node's estimate is below the lowest node
  329.         // on this level
  330.         this->estTopLeft.h = Max (this->estTopLeft.h, Contour[level]);
  331.         
  332.         // if the node has children, iterate over them, giving each an estimated
  333.         // position and then calling this routine recursively
  334.         if (this->numItems)
  335.           {
  336.             for (i = 1; i <= this->numItems; i++)
  337.               {
  338.                   theChild = (CPPPackedTreeNode *)NthChild(i);
  339.                   if (theChild)
  340.                     {
  341.                         if (i == 1)
  342.                           {
  343.                               // estimate the top left corner of the first child node
  344.                             switch (Just) {
  345.                                 case kJustCenter:
  346.                                     SetPt (&theChild->estTopLeft, 
  347.                                             this->estTopLeft.h + (this->nodeSize.h / 2) - (childWidth / 2),
  348.                                               this->estTopLeft.v - levelOffset);
  349.                                     break;
  350.                                     
  351.                                 case kJustLeft:
  352.                                     SetPt (&theChild->estTopLeft,
  353.                                             this->estTopLeft.h, this->estTopLeft.v - levelOffset);
  354.                                     break;
  355.                                     
  356.                                 case kJustRight:
  357.                                     SetPt (&theChild->estTopLeft,
  358.                                             this->estTopLeft.h + this->nodeSize.h - childWidth, 
  359.                                             this->estTopLeft.v - levelOffset);
  360.                                     break;
  361.                             }
  362.  
  363.                             // place its children
  364.                             theChild->CPBBottomTraverse(level+1, Max (this->childSize.v+5, GetBranchLength()));        
  365.                             // accumulate it into the rect we will use to center ourselves
  366.                             newGChildRect = theChild->nodeRect;
  367.                               // accumulate the child into the rect enclosing all children
  368.                             newChildRect = theChild->familyRect;
  369.                           }
  370.                         else
  371.                           {
  372.                               SetPt(&theChild->estTopLeft, 
  373.                                    Contour[level+1],
  374.                                    this->estTopLeft.v - levelOffset);
  375.                             
  376.                             // place its children
  377.                             theChild->CPBBottomTraverse(level+1, Max (this->childSize.v+5, GetBranchLength()));
  378.                             // accumulate it into the rect we will use to center ourselves
  379.                               UnionRect (&newGChildRect, &theChild->nodeRect, &newGChildRect);
  380.                               // accumulate the child into the rect enclosing all children
  381.                               UnionRect (&newChildRect, &theChild->familyRect, &newChildRect);
  382.                           }
  383.                     }
  384.               }
  385.           }
  386.  
  387.         // set the node's rectangle
  388.         SetRect (&newNodeRect, this->estTopLeft.h, 
  389.                   this->estTopLeft.v - this->nodeSize.v,
  390.                   this->estTopLeft.h + this->nodeSize.h,
  391.                   this->estTopLeft.v);
  392.         
  393.         // center this node over all of its children
  394.         if (this->numItems)
  395.           {
  396.             // justify the node with respect to its children
  397.                 switch (Just) {
  398.                   case kJustCenter:
  399.                       CenterRect (&newNodeRect, &newGChildRect, TRUE, FALSE);
  400.                       break;
  401.                   case kJustRight:
  402.                       AlignRect (&newNodeRect, &newGChildRect, kRight, kNone);
  403.                       break;
  404.                   case kJustLeft:
  405.                       AlignRect (&newNodeRect, &newGChildRect, kLeft, kNone);
  406.                       break;
  407.                 }
  408.  
  409.             // build the rectangle which encloses the node and its children
  410.             UnionRect (&newNodeRect, &newChildRect, &newFamilyRect);
  411.           }
  412.         else
  413.           newFamilyRect = newNodeRect;
  414.           
  415.   #include <TreeNodeConds.cp>
  416.  
  417.         this->nodeRect = newNodeRect;
  418.          this->childRect = newChildRect;
  419.          this->familyRect = newFamilyRect;
  420.          this->gChildRect = newGChildRect;
  421.  
  422.         this->notYetPlaced = FALSE;
  423.  
  424.         // update the value of the lowest point in the current level
  425.         Contour[level] = newNodeRect.right + GetNodeMargin();
  426.     }
  427.  
  428. /*-----------------------------------------------------------------*/
  429.  
  430.     void    CPPPackedTreeNode::CPBRightTraverse (short level, short levelOffset)
  431.     /* the recursive traversal part of the CalcFamilyBounds for */
  432.     /* Right-to-left oriented trees */
  433.     {
  434.         CPPPackedTreeNode    *theChild;
  435.         short    childHeight = this->childSize.v;
  436.         long    i;
  437.         justStyle    Just    =    GetJustification();
  438.     
  439.         orientStyle    Orient    =    GetOrientation();
  440.         Rect        emptyRect = {0,0,0,0};
  441.         Boolean    eraseKidJoins = FALSE;
  442.         Rect    OldNodeRect = this->nodeRect,
  443.                 OldGChildRect = this->gChildRect;
  444.         Rect    newNodeRect = {0,0,0,0},
  445.                 newFamilyRect = {0,0,0,0},
  446.                 newChildRect = {0,0,0,0},
  447.                 newGChildRect = {0,0,0,0};
  448.     
  449.         // make sure the node's estimate is below the lowest node
  450.         // on this level
  451.         this->estTopLeft.v = Max (this->estTopLeft.v, Contour[level]);
  452.         
  453.         // if the node has children, iterate over them, giving each an estimated
  454.         // position and then calling this routine recursively
  455.         if (this->numItems)
  456.           {
  457.             for (i = 1; i <= this->numItems; i++)
  458.               {
  459.                   theChild = (CPPPackedTreeNode *)NthChild(i);
  460.                   if (theChild)
  461.                     {
  462.                         if (i == 1)
  463.                           {
  464.                               // estimate the top left corner of the first child node
  465.                             switch (Just) {
  466.                                 case kJustCenter:
  467.                                       SetPt(&theChild->estTopLeft, 
  468.                                           this->estTopLeft.h - levelOffset,
  469.                                             this->estTopLeft.v + (this->nodeSize.v / 2) - (childHeight / 2));
  470.                                     break;
  471.                                     
  472.                                 case kJustTop:
  473.                                     SetPt (&theChild->estTopLeft,
  474.                                             this->estTopLeft.h - levelOffset, this->estTopLeft.v);
  475.                                     break;
  476.                                     
  477.                                 case kJustBottom:
  478.                                     SetPt (&theChild->estTopLeft,
  479.                                             this->estTopLeft.h - levelOffset, 
  480.                                             this->estTopLeft.v + nodeSize.v - childHeight);
  481.                                     break;
  482.                             }
  483.  
  484.                             // place its children
  485.                             theChild->CPBRightTraverse(level+1, Max (this->childSize.h+5, GetBranchLength()));        
  486.                             // accumulate it into the rect we will use to center ourselves
  487.                             newGChildRect = theChild->nodeRect;
  488.                               // accumulate the child into the rect enclosing all children
  489.                             newChildRect = theChild->familyRect;
  490.                           }
  491.                         else
  492.                           {
  493.                               SetPt(&theChild->estTopLeft, 
  494.                                     this->estTopLeft.h - levelOffset,
  495.                                      Contour[level+1]);
  496.                             
  497.                             // place its children
  498.                             theChild->CPBRightTraverse(level+1, Max (this->childSize.h+5, GetBranchLength()));
  499.                             // accumulate it into the rect we will use to center ourselves
  500.                               UnionRect (&newGChildRect, &theChild->nodeRect, &newGChildRect);
  501.                               // accumulate the child into the rect enclosing all children
  502.                               UnionRect (&newChildRect, &theChild->familyRect, &newChildRect);
  503.                           }
  504.                     }
  505.               }
  506.           }
  507.  
  508.         // set the node's rectangle
  509.         SetRect (&newNodeRect, this->estTopLeft.h - this->nodeSize.h, 
  510.                    this->estTopLeft.v,
  511.                    this->estTopLeft.h,
  512.                    this->estTopLeft.v + this->nodeSize.v);
  513.         
  514.         // justify the node with respect to its children
  515.         if (this->numItems)
  516.           {
  517.                 switch (Just) {
  518.                   case kJustCenter:
  519.                       CenterRect (&newNodeRect, &newGChildRect, FALSE, TRUE);
  520.                       break;
  521.                   case kJustTop:
  522.                       AlignRect (&newNodeRect, &newGChildRect, kNone, kTop);
  523.                       break;
  524.                   case kJustBottom:
  525.                       AlignRect (&newNodeRect, &newGChildRect, kNone, kBottom);
  526.                       break;
  527.                 }
  528.             // build the rectangle which encloses the node and its children
  529.             UnionRect (&newNodeRect, &newChildRect, &newFamilyRect);
  530.           }
  531.         else
  532.           newFamilyRect = newNodeRect;
  533.           
  534. #include <TreeNodeConds.cp>
  535.  
  536.         this->nodeRect = newNodeRect;
  537.          this->childRect = newChildRect;
  538.          this->familyRect = newFamilyRect;
  539.          this->gChildRect = newGChildRect;
  540.  
  541.         this->notYetPlaced = FALSE;
  542.  
  543.         // update the value of the lowest point in the current level
  544.         Contour[level] = newNodeRect.bottom + GetNodeMargin();
  545.     }
  546.  
  547. /*-----------------------------------------------------------------*/
  548.  
  549.     void    CPPPackedTreeNode::CPBLeftTraverse (short level, short levelOffset)
  550.     /* the recursive traversal part of the CalcFamilyBounds for */
  551.     /* Left-to-right oriented trees */
  552.     {
  553.         CPPPackedTreeNode    *theChild;
  554.         short    childHeight = this->childSize.v;
  555.         long    i;
  556.         justStyle    Just    =    GetJustification();
  557.         
  558.         orientStyle    Orient    =    GetOrientation();
  559.         Rect        emptyRect = {0,0,0,0};
  560.         Boolean    eraseKidJoins = FALSE;
  561.         Rect    OldNodeRect = this->nodeRect,
  562.                 OldGChildRect = this->gChildRect;
  563.         Rect    newNodeRect = {0,0,0,0},
  564.                 newFamilyRect = {0,0,0,0},
  565.                 newChildRect = {0,0,0,0},
  566.                 newGChildRect = {0,0,0,0};
  567.  
  568.         // make sure the node's estimate is below the lowest node
  569.         // on this level
  570.         this->estTopLeft.v = Max (this->estTopLeft.v, Contour[level]);
  571.         
  572.         // if the node has children, iterate over them, giving each an estimated
  573.         // position and then calling this routine recursively
  574.         if (this->numItems)
  575.           {
  576.             for (i = 1; i <= this->numItems; i++)
  577.               {
  578.                   theChild = (CPPPackedTreeNode *)NthChild(i);
  579.                   if (theChild)
  580.                     {
  581.                         if (i == 1)
  582.                           {
  583.                               // estimate the top left corner of the first child node
  584.                             switch (Just) {
  585.                                 case kJustCenter:
  586.                                       SetPt(&theChild->estTopLeft, 
  587.                                           this->estTopLeft.h + levelOffset,
  588.                                             this->estTopLeft.v + (this->nodeSize.v / 2) - (childHeight / 2));
  589.                                     break;
  590.                                     
  591.                                 case kJustTop:
  592.                                     SetPt (&theChild->estTopLeft,
  593.                                             this->estTopLeft.h + levelOffset, this->estTopLeft.v);
  594.                                     break;
  595.                                     
  596.                                 case kJustBottom:
  597.                                     SetPt (&theChild->estTopLeft,
  598.                                             this->estTopLeft.h + levelOffset, 
  599.                                             this->estTopLeft.v + nodeSize.v - childHeight);
  600.                                     break;
  601.                             }
  602.                                                       
  603.                             // place its children
  604.                             theChild->CPBLeftTraverse(level+1, Max (this->childSize.h+5, GetBranchLength()));        
  605.                             // accumulate it into the rect we will use to center ourselves
  606.                             newGChildRect = theChild->nodeRect;
  607.                               // accumulate the child into the rect enclosing all children
  608.                             newChildRect = theChild->familyRect;
  609.                           }
  610.                         else
  611.                           {
  612.                               SetPt(&theChild->estTopLeft, 
  613.                                    this->estTopLeft.h + levelOffset,
  614.                                     Contour[level+1]);
  615.                             
  616.                             // place its children
  617.                             theChild->CPBLeftTraverse(level+1, Max (this->childSize.h+5, GetBranchLength()));
  618.                             // accumulate it into the rect we will use to center ourselves
  619.                               UnionRect (&newGChildRect, &theChild->nodeRect, &newGChildRect);
  620.                               // accumulate the child into the rect enclosing all children
  621.                               UnionRect (&newChildRect, &theChild->familyRect, &newChildRect);
  622.                           }
  623.                     }
  624.               }
  625.           }
  626.  
  627.         // set the node's rectangle
  628.         SetRect (&newNodeRect, this->estTopLeft.h, this->estTopLeft.v,
  629.                    this->estTopLeft.h + this->nodeSize.h,
  630.                    this->estTopLeft.v + this->nodeSize.v);
  631.         
  632.         // center this node over all of its children
  633.         if (this->numItems)
  634.           {
  635.                 switch (Just) {
  636.                   case kJustCenter:
  637.                       CenterRect (&newNodeRect, &newGChildRect, FALSE, TRUE);
  638.                       break;
  639.                   case kJustTop:
  640.                       AlignRect (&newNodeRect, &newGChildRect, kNone, kTop);
  641.                       break;
  642.                   case kJustBottom:
  643.                       AlignRect (&newNodeRect, &newGChildRect, kNone, kBottom);
  644.                       break;
  645.                 }
  646.             // build the rectangle which encloses the node and its children
  647.             UnionRect (&newNodeRect, &newChildRect, &newFamilyRect);
  648.           }
  649.         else
  650.           newFamilyRect = newNodeRect;
  651.           
  652.  #include <TreeNodeConds.cp>
  653.  
  654.         this->nodeRect = newNodeRect;
  655.          this->childRect = newChildRect;
  656.          this->familyRect = newFamilyRect;
  657.          this->gChildRect = newGChildRect;
  658.  
  659.         this->notYetPlaced = FALSE;
  660.  
  661.         // update the value of the lowest point in the current level
  662.         Contour[level] = newNodeRect.bottom + GetNodeMargin();
  663.     }
  664.  
  665. /*-----------------------------------------------------------------*/
  666.  
  667.     void    CPPPackedTreeNode::PostProcess (Boolean    isVertical)
  668.     /* go back through the tree and make sure that all nodes which */
  669.     /* do not have children are centered between their siblings */
  670.     {
  671.         CPPPackedTreeNode    *tempNode, *leftNode, *rightNode;
  672.         Rect                tempRect, newnodeRect;
  673.         long                rightMost, leftMost;
  674.         short                outerSpan, innerSpan, margin, side;
  675.         Point                newTopLeft;
  676.         
  677.         // the for loop guarantees that all nodes will be visited
  678.         for (long i = 1; i <= numItems; i++)
  679.           {
  680.               tempNode = (CPPPackedTreeNode *)NthChild (i);
  681.             if (tempNode && tempNode->numItems)
  682.                 tempNode->PostProcess (isVertical);
  683.           }
  684.           
  685.         // this section looks at all the children of the visited node
  686.         // and distributes childless nodes between their populated neighbors
  687.         if (this->numItems)
  688.           {
  689.               leftMost = 1;
  690.               while (leftMost < this->numItems)
  691.                 {
  692.                     // advance leftMost to point to the leftmost populated node
  693.                     while ((leftMost <= numItems) && 
  694.                       ((leftNode = (CPPPackedTreeNode *)NthChild(leftMost))->numItems == 0))
  695.                     leftMost++;
  696.                 
  697.                     rightMost = leftMost;
  698.                 
  699.                   if (leftMost >= numItems-1) return;
  700.  
  701.                     innerSpan = 0;
  702.                     // advance rightMost to point to the next populated node
  703.                     rightMost = leftMost+1;
  704.                     while ((rightMost <= numItems) && 
  705.                       ((rightNode = (CPPPackedTreeNode *)NthChild(rightMost))->numItems == 0))
  706.                     {
  707.                         innerSpan += (isVertical) ? rightNode->nodeSize.h : rightNode->nodeSize.v;
  708.                       rightMost++;
  709.                     }
  710.                     
  711.                   if ((rightMost <= numItems) && (rightMost - leftMost > 1))  
  712.                     {
  713.                         // at this point we have at least one childless node between
  714.                         // leftMost and rightMost.  Distribute it/them within the 
  715.                         // boundaries of the left & right nodes by calculating what the
  716.                         // size of the margin between the childless nodes should be
  717.                         if (isVertical)
  718.                           {
  719.                               outerSpan = rightNode->nodeRect.left - leftNode->nodeRect.right;
  720.                               margin = Max (hMargin, (outerSpan - innerSpan) / (rightMost - leftMost));
  721.                               side = leftNode->nodeRect.right;
  722.                               for (long j = leftMost+1; j < rightMost; j++)
  723.                                 {
  724.                                     tempNode = (CPPPackedTreeNode *)NthChild (j);
  725.                                     if (tempNode)
  726.                                       {
  727.                                           newTopLeft = topLeft(tempNode->nodeRect);
  728.                                           newTopLeft.h = side + margin;
  729.                                           tempNode->ShiftNode(newTopLeft);
  730.                                           side = tempNode->nodeRect.right;
  731.                                       }
  732.                                 }
  733.                           }
  734.                         else
  735.                           {
  736.                               outerSpan = rightNode->nodeRect.top - (leftNode->nodeRect.bottom + 1);
  737.                               margin = Min (hMargin, (outerSpan - innerSpan) / (rightMost - leftMost));
  738.                               side = leftNode->nodeRect.bottom;
  739.                               for (long k = leftMost+1; k < rightMost; k++)
  740.                                 {
  741.                                     tempNode = (CPPPackedTreeNode *)NthChild (k);
  742.                                     if (tempNode)
  743.                                       {
  744.                                           newTopLeft = topLeft(tempNode->nodeRect);
  745.                                           newTopLeft.v = side + margin;
  746.                                           tempNode->ShiftNode(newTopLeft);
  747.                                           side = tempNode->nodeRect.bottom;
  748.                                       }    // if tempnode
  749.                                 }    // for k
  750.                           }    // else
  751.                     }    // if rightmost <= numitems...
  752.                     
  753.                   leftMost = rightMost + 1;
  754.                     
  755.                 }    // while
  756.           }    // if this->numitems
  757.     }
  758.  
  759. /*-----------------------------------------------------------------*/
  760.  
  761.     void    CPPPackedTreeNode::DoCalcFamilyBounds (Point TopLeftCorner)
  762.     /* this routine uses the tree drawing algorithm from SAGE */
  763.     /* (Steve Roth @ Carnegie Mellon University) to position the */
  764.     /* nodes in the family */
  765.     {
  766.         orientStyle    Orient = GetOrientation();
  767.  
  768.         // allocate the array to keep track of where nodes can be placed
  769.         Contour = (short *)NewPtrClear(sizeof(short) * (FamilyDepth(0) + 1));
  770.         
  771.         this->estTopLeft = TopLeftCorner;
  772.         
  773.         switch (Orient) {
  774.             case kTopDown    :
  775.                 this->CPBTopTraverse (1, Max(GetBranchLength(), this->nodeSize.v + 5));
  776.                 break;
  777.             case kBottomUp    :
  778.                 this->CPBBottomTraverse(1, Max(GetBranchLength(), this->nodeSize.v + 5));
  779.                 break;
  780.             case kRight2Left :
  781.                 this->CPBRightTraverse(1, Max(GetBranchLength(), this->nodeSize.h + 5));
  782.                 break;
  783.             case kLeft2Right    :
  784.                 this->CPBLeftTraverse(1, Max(GetBranchLength(), this->nodeSize.h + 5));
  785.                 break;
  786.         }
  787.         
  788.         // add a step which goes back and distributes childless nodes
  789.         if (GetJustification() == kJustCenter)
  790.           PostProcess(((Orient == kTopDown) | (Orient == kBottomUp)) ? TRUE : FALSE);
  791.         
  792.         DisposePtr((Ptr)Contour);
  793.     }
  794.  
  795. /*-----------------------------------------------------------------*/
  796.  
  797.     void    CPPPackedTreeNode::ReceiveMessage (CPPGossipMonger *toldBy, 
  798.                                     short reason, void* info)
  799.     {
  800.         CPPPackedTreeNode     *topNode = (this->Root) ? (CPPPackedTreeNode *)this->Root->GetTopMostNode() : NULL;
  801.         Boolean    childChanged, familyChanged;
  802.         RgnHandle    Rgn1, Rgn2;
  803.         static    short    timesCalled = 0;    // tracks recursion
  804.         Rect    oldFamilyRect;
  805.         Point    OldFamilySize;
  806.         
  807.         if (topNode)
  808.           {
  809.               oldFamilyRect = topNode->familyRect;
  810.               OldFamilySize = topNode->familySize;
  811.           }
  812.         else
  813.           {
  814.               SetRect (&oldFamilyRect, 0, 0, 0, 0);
  815.               SetPt (&OldFamilySize, 0, 0);
  816.           }
  817.         
  818.         switch (reason) {
  819.             case kEraseFamily    :
  820.                 EraseNode ((Boolean)info);
  821.                 break;
  822.                 
  823.             case kDrawFamily    :
  824.                 DrawNode ((Boolean)info, TRUE);
  825.                 break;
  826.                 
  827.             case kResizeFamily    :
  828.                 ResizeFamily (TRUE);
  829.                 if (this->Root)
  830.                   ((CPPVisualTree *)this->Root)->AdjustTree();
  831.                 break;
  832.                 
  833.             case kMoveFamily    :
  834.                 MoveFamily ((Point *)info);
  835.                 if (this->Root)
  836.                   ((CPPVisualTree *)this->Root)->AdjustTree();
  837.                 break;
  838.                 
  839.             case kPrepareRemove :
  840.             case kPrepareDelete :
  841.                 ((CPPPackedTreeNode *)info)->EraseNode(TRUE);
  842.                 ApplyToFamily((CPPPackedTreeNode *)info, SetNotYetPlaced, TRUE);
  843.                 break;
  844.  
  845.             case kNodeAdded    :    // the parent of the added node receives this message first
  846.             case kNodeChangedData :
  847.             case kChildDeleted    :
  848.             case kChildRemoved    :    
  849.                 if (this->Root)
  850.                   {
  851.                     ((CPPVisualTree *)this->Root)->Prepare((CPPObject *)1313L);
  852.                     ((CPPVisualTree *)this->Root)->ForceResize (FALSE);
  853.  
  854.                     // erase & redraw ourselves so that the deleted node and its
  855.                     // joins are erased
  856.                       topNode->DrawNode(TRUE, FALSE);
  857.                     ((CPPVisualTree *)this->Root)->DrawAllJoins(TRUE);
  858.                       if ((OldFamilySize.h > topNode->familySize.h) || (OldFamilySize.v > topNode->familySize.v))
  859.                         {
  860.                             Rgn1 = NewRgn();
  861.                             Rgn2 = NewRgn();
  862.                             RectRgn(Rgn1, &oldFamilyRect);
  863.                             RectRgn(Rgn2, &topNode->familyRect);
  864.                             XorRgn (Rgn1, Rgn2, Rgn1);
  865.                             EraseRgn (Rgn1);
  866.                             DisposeRgn(Rgn1);
  867.                             DisposeRgn(Rgn2);
  868.                         }
  869.                       ((CPPVisualTree *)this->Root)->AdjustTree();
  870.                       
  871.                     ((CPPVisualTree *)this->Root)->Restore((CPPObject *)1313L);
  872.                   }
  873.                 break;
  874.                 
  875.             default:
  876.                 CPPTreeNode::ReceiveMessage (toldBy, reason, info);
  877.                 break;
  878.         }
  879.     }
  880.  
  881. /*-----------------------------------------------------------------*/
  882. /*---------------------- PRIVATE METHODS --------------------------*/
  883. /*-----------------------------------------------------------------*/
  884.  
  885.     void    CPPPackedTreeNode::ShiftNode (Point newTopLeft)
  886.     /* Called by PostProcess to move a childless node over to the */
  887.     /* specified position after erasing it and the join to its parent */
  888.     {
  889.         if (!EqualPt(topLeft(this->nodeRect), newTopLeft))
  890.           {
  891.               // erase the node & note that it must be redrawn
  892.               EraseJoin ((CPPPackedTreeNode *)this->Parent, this);
  893.               EraseNode (TRUE);
  894.               this->needsDraw = TRUE;
  895.               
  896.               // move the node and family rects
  897.               OffsetRect (&this->nodeRect, newTopLeft.h - this->nodeRect.left,
  898.                                           newTopLeft.v - this->nodeRect.top);
  899.               this->familyRect = this->nodeRect;
  900.           }
  901.     }
  902.  
  903.